

package unmsm.fingerprint;


import java.awt.Point;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
//Debe eliminar este ultimo
import javax.swing.JOptionPane;

// CFingerPrint: CLASE UTILIZADO PARA REPRESENTAR UN AHUELLA DACTILAR
public class HuellaDactilar {
  //utilizados para la imagen
  //for digital persona with kit
  // kit para la persona digital
  //public int FP_IMAGE_WIDTH = 500; // ancho de la imagen
  //public int FP_IMAGE_HEIGHT = 500;// altura de la imagen
  //para el kit de verificacion
  public int imagenAnchoHuella = 81; //416; //323
  public int imagenAltoHuella = 22;//560;//352
  
  // FP_TEMPLATE_MAX_SIZE: Utilizado para la plantilla: cuidado el tamaño de la matriz ebe ser multiplo de 4 mas 1
  public final int huellaPlantillaTamanoMax = 81; //601;

  //used for matching : usado para la comparacion
  //the max distance between to points when comparing two points to count as a match
  // distancia max de dos puntos para realizar la comparacion
  public int distancia_max_match_dos_puntos = 10;
  
  //the max rotation to use when comparint two points to count as a match
  //rotacion maxima a utilizar al comparar dos puntos
  public int rotacion_max_match_dos_puntos = 10;//10;
  
  //a percentage: un porcenjate
  public int FP_MATCH_THRESHOLD = 55;

  //finger print classifications
  //Wirbel class
  final public int FP_CLASS_WHORL = 1 ;
  //lasso class
  final public int FP_CLASS_LEFT_LOOP = 2;
  final public int FP_CLASS_RIGHT_LOOP = 3;
  final public int FP_CLASS_ARCH = 4;
  final public int FP_CLASS_ARCH_TENTED = 5;

  //fingerprint template values
  //valores de las huellas dactilares
  final private int FP_TEMPLATE_SIZE = 0;
  final private int FP_TEMPLATE_ORIGIN_X = 1;
  final private int FP_TEMPLATE_ORIGIN_Y = 2;
  final private int FP_TEMPLATE_FEATURE_SIZE = 6;
  final private int radio_busqueda_punto = 1;

  //fingerprint origin values
  //final private int FP_ORIGIN_SEARCH_RADIUS = 5;
  //holds skeletinized image

  //BYTE "entero pequeñito" de la huella original respresentada esqueleticamente
  //guarda todo el arreglo de la huella en formatos '1' y '0'
  public byte P[][] = new byte[imagenAnchoHuella][imagenAltoHuella];

  public HuellaDactilar(){
      
  }

  public HuellaDactilar(int width , int height){
    imagenAnchoHuella = width;
    imagenAltoHuella = height;
    P = new byte[width][height];
  }

  public HuellaDactilar(int width , int height ,int MatchPointDistanceMovement,
                      int MatchPointRotationMovment , int MatchThreshold ){
    imagenAnchoHuella = width;
    imagenAltoHuella = height;
    distancia_max_match_dos_puntos = MatchPointDistanceMovement;
    rotacion_max_match_dos_puntos = MatchPointRotationMovment;
    FP_MATCH_THRESHOLD = MatchThreshold;
  }


  public int getImagenAltoHuella() {
        return imagenAltoHuella;
  }

  public void setImagenAltoHuella(int imagenAltoHuella) {
        this.imagenAltoHuella = imagenAltoHuella;
  }

  public int getImagenAnchoHuella() {
        return imagenAnchoHuella;
  }

  public void setImagenAnchoHuella(int imagenAnchoHuella) {
        this.imagenAnchoHuella = imagenAnchoHuella;
  }

  public int getHuellaPlantillaTamanoMax() {
        return huellaPlantillaTamanoMax;
    }

  /************** METODOS PARA C/U DE LAS HUELLAS **************/


  // binarizando la huella dactilar
  public void binarizarHuella(BufferedImage m_image){
   System.out.println("     For doble( largo:416 X ancho:560 )");
   System.out.println("     P[][]; // guarda huella");
   System.out.println("     Formula: ");
   //int val = 0;
   for (int i = 0; i<= imagenAnchoHuella-1; i++){
      System.out.print("        Columna: "+(i+1));
      System.out.print(" :   ");
      for (int j = 0; j<= imagenAltoHuella-1; j++){
        //val++;
        // objeto de color en escala RGB
        Color c = new Color(m_image.getRGB(i,j));
        System.out.print((j+1)+"("+c.getBlue()+","+c.getRed()+","+c.getGreen()+")");
        if ((c.getBlue()  <= 127) && (c.getRed()  <= 127) && (c.getGreen()  <= 127)){
          P[i][j] = 1;
          System.out.print("N  ");
        }
        else{
          P[i][j] = 0;
          System.out.print("B  ");
        }
        //System.out.print(P[i][j]);
      }
     System.out.println();
   }

    //System.out.println("Valor 1 = "+val);
    //val = 0;
    //Estableciendo bordes a '0' a lo ANCHO
    for(int i = 0; i<= imagenAnchoHuella - 1;i++){
      P[i][0] = 0;
      P[i][imagenAltoHuella-1] = 0;
      //val++;
    }
    //System.out.println("Valor 2 = "+val);
    //val = 0;
    //Estableciendo bordes a '0' a lo LARGO
    for(int j = 0 ; j<=  imagenAltoHuella - 1 ; j++){
      P[0][j] = 0;
      P[imagenAnchoHuella-1][j] = 0;
      //val++;
    }
    //System.out.println("Valor 3 = "+val);
  }

 public BufferedImage getFingerPrintImage(){
    BufferedImage m_ImageBuffer = new BufferedImage(imagenAnchoHuella,imagenAltoHuella,BufferedImage.TYPE_INT_RGB );
    for (int i = 0; i<= imagenAnchoHuella - 1;i++){
      for (int j = 0;j<= imagenAltoHuella - 1;j++){
        if (P[i][j] == 1)
          m_ImageBuffer.setRGB(i,j,0);
        else
          m_ImageBuffer.setRGB(i,j,Color.white.getRGB());
      }
    }
    return m_ImageBuffer;
  }


 // para obtener el buffer de la imagen ezqueletizada
 // devuelve en azul las crestas y en blanco los valles
 public BufferedImage getImagenHuellaPintada(){
    //set finger print image
    //establecer imagen de huella dactilar de acuerdo al color BLANCO:0   NEGRO:1
    BufferedImage m_ImageBuffer = new BufferedImage(imagenAnchoHuella,imagenAltoHuella,BufferedImage.TYPE_INT_RGB );
    for (int i = 0; i<= imagenAnchoHuella - 1; i++){
      for (int j = 0; j<= imagenAltoHuella - 1; j++){
        if (P[i][j] == 1)
          m_ImageBuffer.setRGB(i,j,Color.BLACK.getRGB()); //NEGRO: cresta
        else
          m_ImageBuffer.setRGB(i,j,Color.WHITE.getRGB()  );//BLANCO: fondo
      }
    }

    //obtener características
    double m_arr[] = this.getPlantillaHuella();
    //pintar puntos
    Graphics2D gf = m_ImageBuffer.createGraphics();
    gf.setColor(Color.RED);

    for(int i=7 ; i<=m_arr[0]-1;i=i+6){
      if(m_arr[i+4]>1){
        //rectangulo: BIFURCACION
        gf.setColor(Color.RED);
        gf.drawRect((int)m_arr[i]+(int)m_arr[1]-3,(int)m_arr[i+1]+(int)m_arr[2]-2,5,5);
      }
      else if(m_arr[i+4]==1){
        //rectangulo: TERMINACION
        gf.setColor(Color.GREEN);
        gf.drawRect((int)m_arr[i]+(int)m_arr[1]-3,(int)m_arr[i+1]+(int)m_arr[2]-2,5,5);
      }
    }//end for

    gf.setColor(Color.GRAY);

    //draws the origin
    gf.drawLine((int)m_arr[1]-5,(int)m_arr[2],(int)m_arr[1]+5,(int)m_arr[2]);
    gf.drawLine((int)m_arr[1],(int)m_arr[2]-5,(int)m_arr[1],(int)m_arr[2]+5);
    gf.drawImage(m_ImageBuffer,null,imagenAnchoHuella ,  imagenAnchoHuella);
    return m_ImageBuffer;
  }//end void


  //ThinningHilditch: adegazamiento de cresta
  //exclusivamente con el método de Zhang Suen
  public void adelgazamientoCrestasEtapaA(){
    int change  = 1;
    boolean mbool  = true;
    while(change != 0){
      change = 0;
      for(int i = 2; i <= imagenAnchoHuella - 2; i++){
        for(int j = 2; j <= imagenAltoHuella - 2; j++){
          if (P[i][j] == 1){
              //entero pequeñito
              short c  = 0;
              //count surrounding 1
              //contar con un entorno
              //a) Make sure pixel 1, has 2 to 6 (inclusive) neighbors
              // a) Hacer pixel de que 1, tiene 2 al 6 (ambos inclusive) los vecinos
              if (P[i][j+1] == 1) { c++;}
              if (P[i+1][j+1] == 1) { c++;}
              if (P[i+1][j] == 1) { c++;}
              if (P[i+1][j-1] == 1) { c++;}
              if (P[i][j-1] == 1) { c++;}
              if (P[i-1][j-1] == 1) { c++;}
              if (P[i-1][j] == 1) { c++;}
              if (P[i-1][j+1] == 1) { c++;}

              if ((c >= 2) && (c <= 6 )){
                c = 0;
                //b) starting from 2, go clockwise until 9, and count the
                //'   number of 0 to 1 transitions.  This should be equal to 1.
                if ((P[i-1][j+1] == 0) && (P[i][j+1] == 1)) { c++;}
                if ((P[i][j+1] == 0) && (P[i+1][j+1] == 1)) { c++;}
                if ((P[i+1][j+1] == 0) && (P[i+1][j] == 1)) { c++;}
                if ((P[i+1][j] == 0) && (P[i+1][j-1] == 1)) { c++;}
                if ((P[i + 1][j-1] == 0) && (P[i][j-1] == 1)) { c++;}
                if ((P[i][j-1] == 0) && (P[i-1][j-1] == 1)) { c++;}
                if ((P[i-1][j-1] == 0) && (P[i-1][j] == 1)) { c++;}
                if ((P[i - 1][j] == 0) && (P[i-1][j+1] == 1)) { c++;}

                if (c == 1 ){
                    c = 0;
                      if (mbool == true){
                        //c) 2*4*6=0  (ie either 2,4 ,or 6 is off)
                        //c) 2 * 4 * 6 = 0 (es decir, ya sea 2,4 o 6 está apagado)
                        if ((P[i][j+1] * P[i+1][j] * P[i+1][j-1]) == 0 ){
                          //d) 4*6*8=0
                          if ((P[i+1][j] * P[i+1][j-1] * P[i-1][j]) == 0 ){
                            P[i][j] = 0;
                            change++;
                          }
                        }
                        mbool = false;
                      }//fin  if(mbool == true)
                      else{
                        //c) 2*6*8=0
                        if ((P[i][j+1] * P[i+1][j-1] * P[i-1][j]) == 0){
                          //d) 2*4*8=0
                          if ((P[i][j+1] * P[i+1][j] * P[i-1][j]) == 0){
                            P[i][j] = 0;
                            change++;
                          }
                        }
                        mbool = true;
                      }//fin del   else
                }//fin if (c == 1 )
              }//fin  if ((c >= 2) && (c <= 6 ))
           }//fin de  if(P[i][j] == 1)
        }//fin del segundo for()
      }//fin del primer for()
    }//fin de   while(change != 0)
  }//end ThinningHilditchAlgorithim

  //ThinningHitAndMiss: adelgazamiento y correccion de errores
  // a nivel de los contornos de crestas
  public void adelgazamientoCrestasEtapaB(){
   /* basicly you take all patterns
   *  basicamente tu tomas todos los patrones
   *    111     X1X
   *    X1X  ó  x11 etc
   *    000     xxX
   *    if these conditions are true then set the middle 1 to 0
   *    si se cumplen estas condiciones a continuación, establezca el centro en '1'-'0'
   */
  int c  = 1;
  while (c != 0){
    c = 0;
    for(int i = 1;i<=imagenAnchoHuella - 1;i++){
      for(int j = 1;j<=imagenAltoHuella - 1;j++){
         if ((P[i][j] == 1) && (i != 0) && (j != imagenAltoHuella - 1)
             && (j != 0) && (i != imagenAnchoHuella - 1)){
                if ((P[i-1][j-1] == 1) &&( P[i][j-1] == 1) && (P[i+1][j-1] == 1)
                                  && (P[i-1][j+1] == 0) && (P[i][j+1] == 0) && (P[i+1][j+1] == 0)){
                     P[i][j] = 0; //'1 on bottom
                     c++;
                }
                else if ((P[i-1][j+1] == 1) && (P[i][j+1] == 1) && (P[i+1][j+1] == 1)
                                  && (P[i-1][j-1] == 0) && (P[i][j-1] == 0) && (P[i+1][j-1] == 0)){
                     P[i][j] = 0; //'1 on top
                     c++;
                }
                else if ((P[i-1][j] == 1) && (P[i-1][j - 1] == 1) && (P[i-1][j+1] == 1)
                                  && (P[i+1][j] == 0) && (P[i+1][j+1] == 0) && (P[i+1][j-1] == 0)){
                     P[i][j] = 0; //'1 on left
                     c++;
                }
                else if ((P[i+1][j] == 1) && (P[i+1][j-1] == 1) && (P[i+1][j+1] == 1)
                                  && (P[i-1][j] == 0) && (P[i-1][j+1] == 0) && (P[i-1][j-1] == 0) ){
                     P[i][j] = 0; //'1 on right
                     c++;
                }
                else if ((P[i-1][j] == 1) && (P[i][j-1] == 1) && (P[i][j+1] == 0)
                                  && (P[i+1][j+1] == 0) && (P[i + 1][j] == 0)){
                    //x00
                    //110
                    //x1x
                     P[i][j] = 0; //'1 on Bottem Left
                     c++;
                }
                else if ((P[i-1][j] == 1) && (P[i][j+1] == 1) && (P[i][j-1] == 0)
                                  && (P[i+1][j-1] == 0) && (P[i+1][j] == 0)){
                    //x1x
                    //110
                    //x00
                     P[i][j] = 0; //'1 on Top Left
                     c++;
                }
                else if ((P[i][j+1]== 1) && (P[i+1][j] == 1) && (P[i-1][j] == 0)
                                  && (P[i-1][j-1] == 0) && (P[i][j-1] == 0)){
                    //x1x
                    //011
                    //00x
                     P[i][j] = 0; //'1 on Top Right
                     c++;
                }
                else if ((P[i][j-1] == 1) && (P[i+1][j] == 1) && (P[i-1][j] == 0)
                                  && (P[i-1][j+1] == 0) && (P[i][j+1] == 0) ){
                    //00x
                    //011
                    //x1x
                     P[i][j] = 0; //'1 on Bottom Right
                     c++;
                }
         }
      }//siguiente imagenAltoHuella
    }//siguiente imagenAnchoHuella
   }//fin de While
  }//end adelgazamientoCrestasEtapaB

  
  public void ChaneLinkAlgorithm(int ChainLinkDistance){
    //short count1;
    //conteo corto
    for(int i = 1;i<=  imagenAnchoHuella - 1;i++)
    {
      for (int j = 1 ; j <= imagenAltoHuella - 1;j++)
      {
        //change second condition when changeing direction Horizontal
        //cambio segunda condición al cambiar de dirección horizontal
        if ((P[i][j] == 1) && (i != imagenAnchoHuella - 1) && (i != 0) && (j != imagenAltoHuella - 1) && (j != 0))
        {
          if (P[i + 1][j] == 0)
          {
            short countX  = 0;
            //count Horizontal Hole
            //conteo horizontal de agujeros
            while (((i + countX) <= imagenAnchoHuella - 1) && (countX <= ChainLinkDistance))
            {
              if (((i + countX + 1) <= imagenAnchoHuella - 1) && ((countX + 1) <= ChainLinkDistance))
              {
                if (P[i + countX + 1][j] == 0)
                {
                  countX++;
                }
                else
                {
                  break;
                }
              }
              else
              {
                break;
              }

            }
            //Fill hole if it is wide enough
            //llenar el agujeo (bifurcaciones) si es lo suficientemente ancha
            if ((countX != 0) && ((countX + 1) <= ChainLinkDistance))
            {
              for (int temp = 0; temp<= countX;temp++)
              {
                P[i+temp][j] = 1;
              }
            }
          }
      }
      //change second condition when changeing direction Vertical
      //cambio segunda condición al cambiar de dirección vertical
      if ((P[i][j] == 1) && (i != imagenAnchoHuella - 1) && (i != 0) && (j != imagenAltoHuella - 1) && (j != 0))
      {
        if (P[i][j+1] == 0 )
        {
          short countY  = 0;
          //count Horizontal Hole
          //conteo horizontal de agujeros
          while (((j + countY) <= imagenAltoHuella-1) && (countY <= ChainLinkDistance))
          {
            if (((j + countY + 1) <= imagenAltoHuella-1) && ((countY + 1) <= ChainLinkDistance) )
            {
              //input this here bacause it kept on crashing
              //entro aqui porque siguió estrellándose
              if (P[i][j + countY + 1] == 0)
              {
                countY++;
              }
              else
              {
                break;
              }
            }
            else
            {
              break;
            }

          }
          //Fill hole if it is wide enough
          //llenar orificio si es lo suficientemente ancha
          if ((countY != 0) && (countY + 1 <= ChainLinkDistance))
          {
            for(int temp = 0;temp<=  countY;temp++)
            {
              P[i][j + temp] = 1;
            }
          }
        }
      }
      //change second condition when changeing direction Vertical Horizontal
      //cambio segunda condición cuando changeing dirección vertical horizontal
      if ((P[i][j] == 1) && (i != imagenAnchoHuella - 1) && (i != 0) && (j != imagenAltoHuella - 1) && (j != 0))
      {
        if (P[i + 1][j + 1] == 0)
        {
          short countYX  = 0; //1
          //count Horizontal Hole
          //conteo horizontal de agujeros
          while ((j + countYX <= imagenAltoHuella-1) && (i + countYX <= imagenAnchoHuella-1) && (countYX <= ChainLinkDistance))
          {
            if (((j + countYX + 1) <= imagenAltoHuella-1) && ((i + countYX + 1) <= imagenAnchoHuella-1) && ((countYX + 1) <= ChainLinkDistance))
              if (P[i+countYX+1][j + countYX + 1] == 0)
              {
                countYX++;
              }
              else
              {
                break;
              }

              else
              {
                break;
              }
          }
          //Fill hole if it is wide enough
          //llenar orificio si es lo suficientemente ancha
          if ((countYX != 0) && (countYX + 1 <= ChainLinkDistance))
          {
            for(int temp = 0 ; temp <=  countYX ; temp++)
            {
              P[i+temp][j+temp] = 1;
            }
          }
        }
      }
      }
    }
  }

  /*
* ########################################################
* #  obtener el punto de origen "core" - Extract Origin  #
* ########################################################
*
*   In future i want to use the gradients to classifie the finger print into the 5 different
*   catagories which are marked in the FP_CLASS.
*   En el futuro quiero utilizar los gradientes de classifie la huella digital en las
*   5 categorías diferentes que están marcados en el FP_CLASS.
*
*   This function still needs to improved and somtimes dosen't find the center of the finger print.
*   Esta función todavía tiene que mejorar y, a veces no encuentra el centro de la huella digital.
*
*   The principle in finding the centre is simple , just find the greatest change in the gradient
*   bettween two lines and you have your centre.
*   El principio para encontrar el centro es sencillo, basta con encontrar el mayor cambio entre
*   gradiente de dos líneas y que tenga su centro.
*
*   To find the classification you have to find the average changes in gradients in the different
*   sectors (if you divided your picture in 4 using the fingerprint centre as the centre).You should
*   then classifie the fingerprint according to this.
*   Para buscar la clasificación tu tienes que encontrar los cambios medios en los gradientes
*   en los diferentes sectores (si usted divide la imagen en 4 usando el centro de la huella digital
*   como el centro). A continuación, debe clasificar la huella digital de acuerdo con esto.
*/

private Point getPuntoCore(){
  //punto (x,y)
  Point m_Point = new Point();
  double gradcur=0;
  double gradprev=0;
  double gradchangebig=0;
  double gradchange=0;

  double graddistancebig=0;
  double graddistance=0;

  double prevx=0;
  double prevy=0;

  for(int j =50;j<=imagenAltoHuella-50;j++){
    for(int i =50;i<=imagenAnchoHuella-50;i++){
      if(P[i][j]==1){
         //count surrounding pixels
         //contar los píxeles circundantes
         int tc=0;
         int x1=0;
         int y1=0;
         int x2=0;
         int y2=0;
         //find surrounding 1s
         //buscar alrededor 1s
         for(int m = -1*radio_busqueda_punto; m <= radio_busqueda_punto; m++){
           for(int n = -1*radio_busqueda_punto; n<=radio_busqueda_punto; n++){
                 if( (m==radio_busqueda_punto) || (m==(-1)*radio_busqueda_punto)
                     || (n==radio_busqueda_punto) || (n==(-1)*radio_busqueda_punto) ){
                    if(P[i+m][j+n] == 1){
                       tc++;
                       if(tc==1){
                         x1=i+m;
                         y1=j+n;
                       }
                       if (tc==2){
                         x2=i+m;
                         y2=j+n;
                       }
                    }//fin del if
                 }//if
           }//fin del segundo FOR
         } //fin del primer FOR
         //does all the hard work of finding the greatest change in gradient
         // hace todo el trabajo duro de encontrar el mayor cambio en el gradiente
         if(tc==2){
           if((x2-x1)>0){
             gradcur  = (y2-y1)/(x2-x1);
             //check to see gradient change by at least 270 degrees
             //comprobar el cambio de gradiente por lo menos 270 grados
             if((gradcur>0)&&(gradprev<0)){
                gradchange = Math.abs(gradcur) + Math.abs(gradprev) ;
                graddistance = Math.abs(i) - Math.abs(prevx);
                if(gradchangebig<gradchange){
                   if(graddistancebig<graddistance){
                       gradchangebig=gradchange;
                       graddistancebig=graddistance;
                       m_Point.x = i;//FP_ORIGIN_X =i;
                       m_Point.y = j;//FP_ORIGIN_Y =j;
                    }
                 }
                 break;
              }// fin del if conprobante de gradiente
              //reset varibles for new checks
              //restableciendo nuevas variables de control
              gradprev=gradcur;
              gradcur=0;
              prevx=i;
              prevy=j;
           }// fin del  if(x2-x1)>0
         }//fin  if(tc==2)
       }//fin   if(P[i][j]==1)
       }//end for i
      }//end for j
    //  JOptionPane.showMessageDialog (null,Integer.toString(FP_ORIGIN_X)+";"+Integer.toString(FP_ORIGIN_Y),"getFingerPrintOrigin",JOptionPane.PLAIN_MESSAGE);
    return m_Point;
  }

private int getFingerPrintClassification(){
      Point m_Point = this.getPuntoCore();
      double gradcur=0;

      //stores total gradient of corners
      double gradlt=0;
      double gradrt=0;
      double gradlb=0;
      double gradrb=0;

      //counts total of each corner gradient
      double cgradlt=0;
      double cgradrt=0;
      double cgradlb=0;
      double cgradrb=0;

      for(int j =50;j<=imagenAltoHuella-50;j++)
      {
        for(int i =50;i<=imagenAnchoHuella-50;i++)
        {
          if(P[i][j]==1)
           {
            //count surrounding pixels
            int tc=0;
            int x1=0;
            int y1=0;
            int x2=0;
            int y2=0;
            //find surrounding 1s
            for (int m = -1*radio_busqueda_punto;m<=radio_busqueda_punto;m++)
                {
                for (int n = -1*radio_busqueda_punto;n<=radio_busqueda_punto;n++)
                {
                    if ((m==radio_busqueda_punto)||(m==(-1)*radio_busqueda_punto)||(n==radio_busqueda_punto)||(n==(-1)*radio_busqueda_punto))
                    {
                        if(P[i+m][j+n] == 1 )
                        {
                        tc++;
                            if (tc==1)
                            {
                            x1=i+m;
                            y1=j+n;
                            }
                            if (tc==2)
                            {
                            x2=i+m;
                            y2=j+n;
                            }
                        }//end if
                    }//end if
                }//end for n
              } //end for m
              //does all the hard work of finding the greatest change in gradient
              if(tc==2)
              {
               if ((x2-x1)>0)
               {
                    gradcur  = (y2-y1)/(x2-x1);
                    //check to see gradient change by at least 270 degrees
                    if ((x2<m_Point.x)&&(y2>m_Point.y))
                    {
                    gradlt=gradlt+gradcur;
                    gradlt++;
                    }
                    else if ((x2>m_Point.x)&&(y2>m_Point.y))
                    {
                    gradrt=gradrt+gradcur;
                    gradrt++;
                    }
                    else if ((x2<m_Point.x)&&(y2<m_Point.y))
                    {
                    gradlb=gradlb+gradcur;
                    gradlb++;
                    }
                    else if ((x2>m_Point.x)&&(y2<m_Point.y))
                    {
                    gradrb=gradrb+gradcur;
                    gradrb++;
                    }

                 }//(x2-x1)>0
              }//end if tc==2
           }//end if P[x][y]==1
       }//end for i
      }//end for j
      //get average gradient for 4 corners
      gradlb=gradlb/cgradlb;
      gradrb=gradrb/cgradrb;
      gradlt=gradlt/cgradlt;
      gradrt=gradrt/cgradrt;
      //determin classification according to gradient
      //needs work
      if ((gradlt>0)&&(gradrt>0)&&(gradlb>0)&&(gradrb>0))
      {
      return FP_CLASS_WHORL;
      }
      else if ((gradlt>0)&&(gradrt>0)&&(gradlb>0)&&(gradrb>0))
      {
      return FP_CLASS_LEFT_LOOP;
      }
      else if ((gradlt>0)&&(gradrt>0)&&(gradlb>0)&&(gradrb>0))
      {
      return FP_CLASS_RIGHT_LOOP;
      }
      else if ((gradlt>0)&&(gradrt>0)&&(gradlb>0)&&(gradrb>0))
      {
      return FP_CLASS_ARCH;
      }
      else if ((gradlt>0)&&(gradrt>0)&&(gradlb>0)&&(gradrb>0))
      {
      return FP_CLASS_ARCH_TENTED;
      }
      else
      {
        return 1;
      }
  //  JOptionPane.showMessageDialog (null,Integer.toString(FP_ORIGIN_X)+";"+Integer.toString(FP_ORIGIN_Y),"getFingerPrintOrigin",JOptionPane.PLAIN_MESSAGE);
 }



/************************************************************************************
 La plantilla tendrá que ser con formato de acuerdo a las normas ISO que figuran
 comprar NIST, el NIST también tiene un conjunto de imágenes binarias de usar para
 los ejemplos. Esta base de datos se utiliza para determaning las FAR (Tasa de
 Aceptación Falsa) y FRR (tasa de rechazo por) En primer lugar son 7 (elementos de
 array, originx, originy, null, null, null, null). El formato es (x, y, r, el grado,
 el número de extremos, el grado resultante) el elemento 0 de la matriz es el tamaño.
 También existe un trabajo futuro que hay que hacer en genralization, básicamente lo
 que esto significa es que usted toma 3 dedos plantillas, luego tomar las 
 características  que son comunes a todos templeate tres y, a continuación, saldrá
 con un template. Así mejorará la calidad de la plantilla.
 ************************************************************************************/

//getFingerPrintTemplate:  EXTRAENDO LA PLANTILLA
public double[] getPlantillaHuella(){
   // final int SEARCH_RADIUS = 1;
   // final int buscar radio = 1;
    double x=0;
    double y=0;
    double r=0;
    double d=0;
    // FP_TEMPLATE_MAX_SIZE: valor de la plantilla multiplo de 4 +1 = 601
    double m_arr[] = new double[huellaPlantillaTamanoMax];

    // adelagazamiento de crestas
    this.adelgazamientoCrestasEtapaA();
    this.adelgazamientoCrestasEtapaB();
    // adelagazamiento de crestas
    this.adelgazamientoCrestasEtapaA();
    this.adelgazamientoCrestasEtapaB();

    Point origin = this.getPuntoCore();
    System.out.println("Punto de origen :"+origin.x +","+origin.y);
    m_arr[1] = origin.x;
    m_arr[2] = origin.y;

    int c = 7 ;
    int previ=0;
    int prevj=0;

    boolean primeraEntrada = true;

    //start from 5 units in to avoid detection of edges of finger print and out of bound exceptions
    //inicio de 5 unidades para evitar la detección de los bordes de la huella digital y 
    //fuera de las excepciones de la envolvente
    for(int j = 5; j<= imagenAltoHuella - 6; j++){
      primeraEntrada = true;
       for( int i =5; i<= imagenAnchoHuella - 6; i++){
       if ((c<huellaPlantillaTamanoMax)&&(P[i][j] == 1) && (i != imagenAnchoHuella - 1)
               && (i != 0) && (j != imagenAltoHuella - 1) && (j != 0) ){
         /*
         *   Must not capture first and last feature because those are the edges of the finger print
         *   and will provide no value to the template.
         *
         *   No debe capturar la primera y ultima caracteristica por que esos son los bordes
         *   de la huella digital y sin valor para la plantilla.
         */
         if(primeraEntrada == true){
            primeraEntrada = false;
            //check to see if previos item in array was aslo end
            //comprobar para ver si el artículo Previo en el arsenal fue también terminal
            if( (c>7)&&((m_arr[c-6]+origin.x)==previ)&&((m_arr[c-5]+origin.y)==prevj) ){
              //delete previos featue
              //eliminar caracteristicas iniciales
                m_arr[c--]=0;
                m_arr[c--]=0;
                m_arr[c--]=0;
                m_arr[c--]=0;
                m_arr[c--]=0;
                m_arr[c--]=0;
            }
         }
         else{
              int tc = 0;
              for (int m = -1*radio_busqueda_punto;m<=radio_busqueda_punto;m++){
                for (int n = -1*radio_busqueda_punto;n<=radio_busqueda_punto;n++){
                    if ((m==radio_busqueda_punto)||(m==(-1)*radio_busqueda_punto)
                            ||(n==radio_busqueda_punto)||(n==(-1)*radio_busqueda_punto)){
                      if(P[i+m][j+n] == 1 ){
                           tc++;
                      }//end if
                    }//end if
                }//end for n
              } //end for m

              //calculate parameters necesary for template
              //calcular los parámetros necesarios para la plantilla
              if ((tc==1)||(tc==3)){
                  x = i- origin.x;
                  y = j- origin.y;
                  r = Math.hypot(x,y);
                  if ((x>0)&&(y>0)){
                     d =Math.atan(y/x);
                  }
                  else if ((x<0)&&(y>0)){
                     d =Math.atan(y/x) - Math.PI ;
                        }
                  else if ((x<0)&&(y<0)){
                     d =Math.PI + Math.atan(y/x);
                  }
                  else if ((x>0)&&(y<0)){
                     d =2*Math.PI + Math.atan(y/x);
                  }
              }

              //check to see if point already been captured
              //comprobar para ver si el punto ya ha sido capturado
              boolean foundx = false;
              boolean foundy = false;
              
              for(int m = 7; m <= c; m = m+6){
                  if(m_arr[m+4]==3){
                     if(Math.abs(Math.abs((int)m_arr[m])-Math.abs(x))<4){
                         foundx=true;
                     }
                     if(Math.abs(Math.abs((int)m_arr[m+1])-Math.abs(y))<4){
                         foundy=true;
                     }
                  }//end if
              }//end for m


              //1 surrounding 1
              //1 alrededor 1s
              if ((tc==1) && (c <= huellaPlantillaTamanoMax-6)  && (x!=0)
                      && (y!=0) && ((foundx==false)||(foundy==false)) ){
                  if (P[i-1][j+1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] = 135 ;
                  }
                  else if (P[i][j+1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] =90  ;
                  }
                  else if (P[i+1][j+1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] =45  ;
                    }
                  else if (P[i+1][j] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] = 0  ;
                    }
                  else if (P[i+1][j-1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] =315  ;
                  }
                  else if (P[i][j-1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] = 270  ;
                    }
                  else if (P[i-1][j-1] == 1){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] = 225 ;
                  }
                  else if (P[i-1][ j] == 1 ){
                    m_arr[c++] = x;
                    m_arr[c++] = y;
                    m_arr[c++] = r;
                    m_arr[c++] = d;
                    m_arr[c++] = 1 ;
                    m_arr[c++] = 180 ;
                  }
                }//fin de primer IF
                else if((tc>=3) && (c <= huellaPlantillaTamanoMax - 6)
                          && (x!=0) && (y!=0) && ((foundx==false)||(foundy==false)) ){
                  //3 surrounding 1s
                  //3 rodea 1s
                  m_arr[c++] = x;
                  m_arr[c++] = y;
                  m_arr[c++] = r;
                  m_arr[c++] = d;
                  m_arr[c++] = 3;
                  m_arr[c++] = 0;
                 }//fin del IF ELSE, if tc>=3
       }//end if first

       previ=i;
       prevj=j;
       //306;269
       if(((i- origin.x)>=(306 - 4))&&((i- origin.y)>=(269-4))){
         if (((i- origin.x)<=(306+4))&&((i- origin.y)<=(269+4))){
             JOptionPane.showMessageDialog (null,Double.toString(c)+";"+Integer.toString(i)+";"+Integer.toString(j),"Mi punto es: ",JOptionPane.PLAIN_MESSAGE);
         }
       }

          }//end if that checks for p[x,y]=1
           //final si que los controles para p [x, y] = 1
      }//end for
    }//end for
    //put total size of points collected at 0 in array
    // Poner el tamaño total de puntos obtenidos a 0 en el arsenal
    m_arr[0]=c;
    return m_arr;
  }//end getFingerPrintTemplate()


  public String ConvertFingerPrintTemplateDoubleToString(double[] finger){
     String temp="";
     System.out.println("EMPIEZAA");
     for (int i=0;i<=finger.length-1 ; i++){
        temp = temp +Double.toString(finger[i])+";";
        System.out.println(finger[i]);
     }
    return temp;
  }

  public double [] ConvertFingerPrintTemplateStringToDouble(String finger){
        double m_finger[] = new double[huellaPlantillaTamanoMax];
        int c=-1;
        String m_double = "";
        String temp ="";
        for (int i=0;i<=finger.length()-1 ; i++)
        {
           temp = Character.toString(finger.charAt(i));
           if (temp.equals(";"))
           {
               m_finger[c++] = Double.parseDouble(m_double);
           }
           else
           {
            m_double  = m_double +  temp;
           }
        }
    return m_finger;
  }

/*
* ################################
* #    comparcion -  Matching    #
* ################################
*
*   Something to possably look at are
*   Algo que buscar posiblemente en son
*
*   Distance = (X1 -X2)^2 + (Y1 - Y2)^2. The Error_Rating , if a image is is to the left or
*   right or even at a angle the distance betwwen matched points will always be the same.
*   Distancia = (X1 -X2)^2 + (Y1 - Y2)^2. La Clasificación de error, si una imagen se está a la 
*   izquierda o a la derecha o incluso en un ángulo de la distancia entre los puntos encontrados siempre será el mismo.
*
*    cross-corelation algorithm
*    algoritmo de correlacion cruzada
*
*    The algotrithim could use classifications to speed it up and check that the feature
*    direction to improve the faulse acceptance rate.
*    El algoritmo puede utilizar las clasificaciones para acelerar y comprobar que la
*    dirección de función para mejorar la tasa de falsa aceptación.
*/


  public int Match(double[] finger1,double[] finger2 , int threshold , boolean fastmatch){
    //compare matrix with all shifted matrixes
    //must do later. must get the size of the array
    //comparar la matriz con todas las matrices
    //desplazado debe hacer más adelante. debe obtener el tamaño de la matriz

      //JOptionPane.showMessageDialog (null,Double.toString(finger1[0])+";"+Double.toString(finger1[1])+";"+Double.toString(finger2[3]),"Match",JOptionPane.PLAIN_MESSAGE);
    double matchcount = 0;
    double matchcounttotal = (finger1[0]-6)/6;
    double bestmatch =0;
    double radian = Math.PI/180;
    boolean foundpoint;

    for (int k = -1*rotacion_max_match_dos_puntos; k <= rotacion_max_match_dos_puntos; k++){
          for (int i =7; i <= finger1[0]-5; i=i+6){
            foundpoint = false;
            for (int j =7; j <= finger2[0]-5; j=j+6){
                if(foundpoint==false){
                //compare two points account for rotational , verticle and horizontal shift
                //comparar dos puntos representan del verticle de rotación y desplazamiento horizontal
                int resx=0;
                int resy=0;
                double x1=0 ;
                double y1=0;
                double x2=0;
                double y2=0;
                double r=0;
                double d=0;
                //find nessasary parameters
                //encontrar los parámetros necesarios

                r =finger2[j+2];
                d = finger2[j+3];
                x2=finger1[i];
                y2=finger1[i+1];
                //do angle shift for x
                //ángulo se muevan para X
                x1 = r*Math.cos(d+(k*radian));
                resx = Math.abs((int)x2 + (int)(-1*x1));
                //do angle shift for y
                //ángulo se muevan para Y
                y1 = r*Math.sin(d+(k*radian));
                resy = Math.abs((int)y2 + (int)(-1*y1));

                //cheak shift matchs count as match
                //verificacion de cambio recuento de la comparacion
                if((distancia_max_match_dos_puntos > resx) && (distancia_max_match_dos_puntos > resy)){
                       //cheak if same kind of feature
                       //compruebe si es la misma clase de característica
                      if(finger1[i+4] == finger2[j+4]){
                           //cheak if feature in  same direction
                           //compruebe si característica en la misma dirección
                         //  if(((finger1[i+5]-finger2[j+5])<=46)||((finger1[i+5]==0)&&(finger2[j+5]==315))||((finger1[i+5]==0)&&(finger2[j+5]==45)))
                         //  {
                            matchcount++;
                            foundpoint = true;
                            //break;
                         //  }//cheak if feature in  same direction
                              //compruebe si característica en la misma dirección
                       } //cheak if same kind of feature
                         //comprobar si mismo tipo de función o carateristica
                    }//end if
                }//if found
            }//end for j
        }//end for i
        //see if we have a match
        //observar si tenemos una comparacion
        //FP_MATCH_THRESHOLD: valor del umbral de comparacion
        if ((((matchcount/matchcounttotal)*100)>=threshold)&&(fastmatch == true)){
            //found match
            //se ha encontrado comparacion
            return (int)((matchcount/matchcounttotal)*100);
        }
        else{
          //not found match
          //No se ha encontrado comparacion
            if(matchcount>bestmatch){
            bestmatch = matchcount;
            }
            //reset match counter to 0
            // resetear contador de comparacion a '0'
            matchcount=0;
        } //end if

    }//end for k
    return (int)((bestmatch/matchcounttotal)*100);
   }//end Match


  /*
   Los procesos por debajo de procesar la imagen hasta que está listo para ser
   pasado al adelgazamiento. Así que se trata entonces las imágenes borrosas
   y posteriormente se binariza.

   the below processes process the image till it is ready to be passed to the
   thinning. So this involves blurring and then binerizing.
   */
}
